#include <stdlib.h>
#include "raylib.h"

// 游戏状态 开始-游戏中-结束
typedef enum GAME_STATE {
    GAME_STATE_START,  GAME_STATE_PLAYING,  GAME_STATE_OVER, 
} GAME_STATE;

// 移动方向
typedef enum DIRECTION {
    DIRECTION_NONE = -1,
    DIRECTION_RIGHT, DIRECTION_DOWN,
    DIRECTION_LEFT, DIRECTION_UP,
} DIRECTION;

// 行列的位置
typedef struct PosRowCol { int row; int col; } PosRowCol;

// 贪吃蛇节点
typedef struct SnakeNode {
    PosRowCol prc;          // 行列
    struct SnakeNode* next; // 下一个节点
} SnakeNode, *PSnakeNode;

// 贪吃蛇 结构体
typedef struct Snake {
    PSnakeNode head;         // 头结点
    DIRECTION moveDirection; // 移动方向
    float moveInterval;      // 移动间隔
} Snake;

#define ROWS 8                                 // 总行数
#define COLS 8                                 // 总列数
const int CELL_WIDTH = 62;                     // 格子宽度
const int CELL_HEIGHT = 62;                    // 格子高度
const int CELL_SPACE = 1;                      // 格子间距
const float MOVE_INTERVAL = .4f;               // 移动间隔
const DIRECTION INIT_DIRECTION = DIRECTION_UP; // 初始移动方向
const PosRowCol INIT_POS = (PosRowCol){5, 3};  // 初始位置

// 变量
Texture textCell;  // 格子纹理
Texture textHead;  // 蛇头纹理
Texture textBody;  // 蛇身纹理
Texture textFood;  // 食物纹理
Texture textStart; // 开始按钮纹理
Texture textRestart; // 重新开始按钮
Snake snake;                             // 贪吃蛇
PosRowCol foodPos;                       // 食物位置
GAME_STATE gameState = GAME_STATE_START; // 游戏状态

void init();                // 程序初始化
void initGame();            // 初始化游戏
void loadTextures();        // 加载纹理
void drawBg();              // 绘制背景和格子
void drawStart();           // 绘制开始按钮
void driveSnake(float dt);  // 驱动贪吃蛇
void drawSnake();           // 绘制贪吃蛇
void drawFood();            // 绘制食物
void drawOver();            // 绘制结束界面
void randomFood();          // 随机放置食物
void checkKeyDown();        // 检测按键
bool showButton(Texture text, Vector2 pos);

Vector2 getPosOfRowCol(int row, int col); // 获取行列的坐标
PosRowCol getNextPrc(PosRowCol, DIRECTION direction);

int main() {
    init();
    while (!WindowShouldClose()) {
        checkKeyDown();
        BeginDrawing();
            drawBg();
            driveSnake(GetFrameTime());
            drawSnake();
            drawFood();
            drawOver();
            drawStart();
        EndDrawing();
    }
    CloseWindow();
    return 0;
}

void init() {
    InitWindow(600, 600, "Snake Game");
    SetTargetFPS(60);
    loadTextures();
    initGame();
}

void initGame() {
    // 清理贪吃蛇节点内存
    PSnakeNode snakeHead = snake.head;
    while (snakeHead != NULL) {
        free(snakeHead);
        snakeHead = snakeHead->next;
    }
    snakeHead = NULL;

    // 初始化贪吃蛇
    snake.head = malloc(sizeof(SnakeNode));
    snake.head->prc = INIT_POS;
    snake.head->next = NULL;
    snake.moveDirection = INIT_DIRECTION;
    snake.moveInterval = 0;
    gameState = GAME_STATE_START;
    randomFood();
}

void loadTextures() {
    textCell = LoadTexture("res/cell.png");
    textHead = LoadTexture("res/head.png");
    textBody = LoadTexture("res/body.png");
    textFood = LoadTexture("res/food.png");
    textStart = LoadTexture("res/start.png");
    textRestart = LoadTexture("res/restart.png");
}

void drawBg() {
    ClearBackground((Color){231, 231, 231, 255});
    for (int row = 0; row < ROWS; row++) {
        for (int col = 0; col < COLS; col++) {
            Vector2 pos = getPosOfRowCol(row, col);
            DrawTexture(textCell, pos.x, pos.y, WHITE);
        }
    }
}

void checkKeyDown() {
    if (gameState != GAME_STATE_PLAYING) return;

    DIRECTION direct = DIRECTION_NONE;
    if (IsKeyPressed(KEY_LEFT) || IsKeyPressed(KEY_A)) direct = DIRECTION_LEFT;
    else if (IsKeyPressed(KEY_RIGHT) || IsKeyPressed(KEY_D)) direct = DIRECTION_RIGHT;
    else if (IsKeyPressed(KEY_UP) || IsKeyPressed(KEY_W)) direct = DIRECTION_UP;
    else if (IsKeyPressed(KEY_DOWN) || IsKeyPressed(KEY_S)) direct = DIRECTION_DOWN;

    if (direct == DIRECTION_NONE) return;

    if (snake.head->next == NULL) {
        snake.moveDirection = direct;
    } else {
        PosRowCol nextPos = getNextPrc(snake.head->prc, direct);
        PosRowCol firstBodyPrc = snake.head->next->prc;
        if (nextPos.row != firstBodyPrc.row || nextPos.col != firstBodyPrc.col) {
            snake.moveDirection = direct;
        }
    }
}

void drawStart() {
    if (gameState != GAME_STATE_START) return;

    DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), (Color){0, 0, 0, 128});

    Vector2 pos = (Vector2){
        GetScreenWidth() / 2 - textStart.width / 2,
        GetScreenHeight() / 2 - textStart.height / 2
    };
    if (showButton(textStart, pos)) gameState = GAME_STATE_PLAYING;
}

Vector2 getPosOfRowCol(int row, int col) {
    Vector2 startPos = (Vector2){46, 51};
    int x = startPos.x + (col * (CELL_WIDTH + CELL_SPACE));
    int y = startPos.y + (row * (CELL_HEIGHT + CELL_SPACE));
    return (Vector2){x, y};
}

PosRowCol getNextPrc(PosRowCol prc, DIRECTION direction) {
    if (direction == DIRECTION_UP) return (PosRowCol){prc.row - 1, prc.col};
    if (direction == DIRECTION_DOWN) return (PosRowCol){prc.row + 1, prc.col};
    if (direction == DIRECTION_LEFT) return (PosRowCol){prc.row, prc.col - 1};
    if (direction == DIRECTION_RIGHT) return (PosRowCol){prc.row, prc.col + 1};
    return prc;
}

void driveSnake(float dt) {
    if (gameState != GAME_STATE_PLAYING) return;

    snake.moveInterval += dt;
    if (snake.moveInterval > MOVE_INTERVAL) {
        snake.moveInterval -= MOVE_INTERVAL;

        // 下个要移动的位置
        PosRowCol nextPos = getNextPrc(snake.head->prc, snake.moveDirection);
        // 如果是边界，游戏结束
        if (nextPos.row < 0 || nextPos.row >= ROWS || nextPos.col < 0 || nextPos.col >= COLS) {
            gameState = GAME_STATE_OVER;
            return;
        }

        // 如果是蛇身，游戏结束
        PSnakeNode snakeHead = snake.head;
        while (snakeHead != NULL) {
            if (nextPos.row == snakeHead->prc.row && nextPos.col == snakeHead->prc.col) {
                gameState = GAME_STATE_OVER;
                return;
            }
            snakeHead = snakeHead->next;
        }

        // 如果是食物，蛇长一节
        if (nextPos.row == foodPos.row && nextPos.col == foodPos.col) {
            PSnakeNode newNode = malloc(sizeof(SnakeNode));
            newNode->prc = nextPos;
            newNode->next = snake.head;
            snake.head = newNode;
            randomFood();
            return;
        }

        // 正常移动
        PosRowCol prevPos = snake.head->prc;
        snake.head->prc = nextPos;
        PSnakeNode body = snake.head->next;
        while (body != NULL) {
            PosRowCol tmp = body->prc;
            body->prc = prevPos;
            prevPos = tmp;
            body = body->next;
        }
    }
}

// 显示贪吃蛇
void drawSnake() {
    Vector2 posHead = getPosOfRowCol(snake.head->prc.row, snake.head->prc.col);
    Vector2 posHeadCenter = { posHead.x + CELL_WIDTH / 2, posHead.y + CELL_HEIGHT / 2 };

    DrawTexturePro(textHead,
        (Rectangle){0, 0, textHead.width, textHead.height},
        (Rectangle){posHeadCenter.x, posHeadCenter.y, textHead.width, textHead.height},
        (Vector2){textHead.width / 2, textHead.height / 2}, 
        snake.moveDirection * 90, 
        WHITE
    );

    PSnakeNode pSnakeBody = snake.head->next;
    while (pSnakeBody != NULL) {
        Vector2 posBody = getPosOfRowCol(pSnakeBody->prc.row, pSnakeBody->prc.col);
        DrawTextureV(textBody, posBody, WHITE);
        pSnakeBody = pSnakeBody->next;
    }
}

void drawFood() {
    Vector2 pos = getPosOfRowCol(foodPos.row, foodPos.col);
    pos.x += 5;
    pos.y += 5;
    DrawTextureV(textFood, pos, WHITE);
}

void drawOver() {
    if (gameState != GAME_STATE_OVER) return;
    DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), (Color){0, 0, 0, 128});
    int startx = (GetScreenWidth() - 94) / 2;
    int starty = (GetScreenHeight() - 30) / 2;
    if (showButton(textRestart, (Vector2){startx, starty})) initGame();
}

void randomFood() {
    int len = 0;
    PosRowCol freePoses[ROWS * COLS];

    for (int row = 0; row < ROWS; row++) {
        for (int col = 0; col < COLS; col++) {
            bool isFree = true;
            PSnakeNode snakeHead = snake.head;
            while (snakeHead != NULL) {
                if (snakeHead->prc.row == row && snakeHead->prc.col == col) {
                    isFree = false;
                    break;
                }
                snakeHead = snakeHead->next;
            }

            if (isFree) {
                freePoses[len] = (PosRowCol){row, col};
                len++;
            }
        }
    }

    foodPos = freePoses[GetRandomValue(0, len - 1)];
}

bool showButton(Texture text, Vector2 pos) {
    Color color = WHITE;
    Rectangle rec = (Rectangle){pos.x, pos.y, text.width, text.height};
    Vector2 mousePoint = GetMousePosition();
    bool clicked = false;
    if (CheckCollisionPointRec(mousePoint, rec)) {
        if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) {
            color = (Color){127, 127, 127, 255};
        } else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) {
            clicked = true;
        } else {
            color = (Color){200, 200, 200, 255};
        }
    }
    DrawTexture(text, pos.x, pos.y, color);
    return clicked;
}
